﻿///<reference path="~/scripts/NinJa.js" />

var NinJaVisuals = {
    Version: "1.2",
    ZIndex: 1000000
};

var Speed = LazyEnum({
    Slow: 400,
    Normal: 300,
    Fast: 200,
    Faster: 100,
    Fastest: 10
});

var Smoothness = LazyEnum({
    Normal: 35,
    Smooth: 40,
    Smoother: 45,
    Smoothest: 50
});

var Alignment = LazyEnum({
    Top: 0,
    Bottom: 1,
    Left: 2,
    Right: 3,
    Center: 4,
    Middle: 5
});

var Side = LazyEnum({
    Top: 0,
    Bottom: 1,
    Left: 2,
    Right: 3
});

var Direction = LazyEnum({
    Vertical: 0,
    Horizontal: 1,
    Diagonal: 2
});

var Effects = LazyEnum({
    None: "none",
    Fade: "fade",
    Slide: "slide",
    FoldVertical: "foldv",
    FoldHorizontal: "foldh",
    FoldBoth: "foldd"
});

var Edge = LazyEnum({
    NorthWest: Cursor.NorthWest,
    SouthEast: Cursor.SouthEast,
    SouthWest: Cursor.SouthWest,
    NorthEast: Cursor.NorthEast,
    North: Cursor.North,
    West: Cursor.West,
    South: Cursor.South,
    East: Cursor.East
});

NinJaVisuals.TextHeight = function (t, f, s) {
    var d = new Span();
    f = f || "default";
    s = s || "default";
    d.Position("absolute").Style(Style.Padding, "0").Style(Style.Margin, "0").Style(Style.FontFamily, f).Style(Style.FontSize, s + "pt").Text(t);
    Page.AddElement(d);
    var s = d.Height();
    Page.RemoveElement(d);
    return s;
};

NinJaVisuals.TextWidth = function (t, f, s) {
    var d = new Span();
    f = f || "default";
    s = s || "default";
    d.Position("absolute").Style(Style.Padding, "0").Style(Style.Margin, "0").Style(Style.FontFamily, f).Style(Style.FontSize, s + "pt").Text(t);
    Page.AddElement(d);
    var s = d.Width();
    Page.RemoveElement(d);
    return s;
};

function Effect(e, t, d, f, p, m) {
    this.Data1 = d;
    this.Data2 = f;
    this.Data3 = this.Data4 = 0;
    this.Type = t;
    this.Speed = p || Speed.Normal;
    this.Smoothness = m || Smoothness.Normal;
    this.Element = e;
    if (t == Effects.Fade) {
        this.Element.Opacity(0);
    }
}


Effect.prototype.Play = function (o) {
    switch (this.Type) {
        case Effects.None:
            this.Element.Collapsed(false);
            if (o) {
                o();
            }
            break;

        case Effects.Fade:
            this.Element.Opacity(0);
            this.Element.FadeTo(this.Data1, this.Speed, this.Smoothness, o);
            break;

        case Effects.Slide:
            var x = this.Data1, y = this.Data2;
            this.Data3 = this.Element.X();
            this.Data4 = this.Element.Y();
            this.Element.Slide(this.Data1, this.Data2, this.Speed, this.Smoothness, o);
            this.Data1 = this.Data3;
            this.Data2 = this.Data4;
            this.Data3 = x;
            this.Data4 = y;
            break;

        case Effects.FoldVertical:
            this.Element.Unfold(Direction.Vertical, this.Speed, this.Smoothness, o);
            break;

        case Effects.FoldHorizontal:
            this.Element.Unfold(Direction.Horizontal, this.Speed, this.Smoothness, o);
            break;


        case Effects.FoldBoth:
            this.Element.Unfold(Direction.Diagonal, this.Speed, this.Smoothness, o);
            break;
    }
}

Effect.prototype.Reverse = function (o) {
    switch (this.Type) {
        case Effects.None:
            this.Element.Collapsed(true);
            if (o) {
                o();
            }
            break;

        case Effects.Fade:
            this.Element.FadeOut(this.Speed, this.Smoothness, o);
            break;

        case Effects.Slide:
            var x = this.Data1, y = this.Data2;
            this.Data3 = this.Element.X();
            this.Data4 = this.Element.Y();
            this.Element.Slide(this.Data1, this.Data2, this.Speed, this.Smoothness, o);
            this.Data1 = this.Data3;
            this.Data2 = this.Data4;
            this.Data3 = x;
            this.Data4 = y;
            break;

        case Effects.FoldVertical:
            this.Element.Fold(Direction.Vertical, this.Speed, this.Smoothness, o);
            break;

        case Effects.FoldHorizontal:
            this.Element.Fold(Direction.Horizontal, this.Speed, this.Smoothness, o);
            break;

        case Effects.FoldBoth:
            this.Element.Fold(Direction.Diagonal, this.Speed, this.Smoothness, o);
            break;
    }
}

// Animations
Element.prototype.Slide = function (x, y, p, m, o) {
    if (this.SlideInfo) {
        this.SlideInfo.Timer.Stop();
    }

    this.SlideInfo = new SlideInfo(this, x, y, p, m, o);
    this.SlideInfo.Timer.Start();
    return this;
}

SlideInfo.Animate = function (s) {
    s.CurrentX += s.IncrementX;
    s.CurrentY += s.IncrementY;
    s.Element.X(s.CurrentX.Round(0));
    s.Element.Y(s.CurrentY.Round(0));
    if ((s.IncrementX > 0 ? s.CurrentX >= s.StopX : s.CurrentX <= s.StopX) && (s.IncrementY > 0 ? s.CurrentY >= s.StopY : s.CurrentY <= s.StopY)) {
        s.Timer.Stop();
        s.Element.X(s.StopX);
        s.Element.Y(s.StopY);
        if (s.OnFinish !== undefined) {
            s.OnFinish();
        }
        s.Element.s = undefined;
    }
}

function SlideInfo(e, x, y, p, m, o) {
    this.Element = e;
    this.StopX = x;
    this.StopY = y;
    this.CurrentX = e.X();
    this.CurrentY = e.Y();
    p = p == undefined ? Speed.Normal : p;
    m = m == undefined ? Smoothness.Normal : m;
    this.OnFinish = o;
    this.IncrementX = (x - this.CurrentX) / m;
    this.IncrementY = (y - this.CurrentY) / m;
    this.Timer = new Timer(new TimeSpan(0, 0, 0, p / m), true);
    this.Timer.SynchronousTrigger = false;
    this.Timer.OnTrigger.Add(new Delegate(SlideInfo.Animate, null, this));
}

Element.prototype.FadeIn = function (p, m, o) {
    if (this.FadeInfo) {
        this.FadeInfo.Timer.Stop();
    }

    this.FadeInfo = new FadeInfo(this, 1, p, m, o);
    this.FadeInfo.Timer.Start();
    return this;
}

Element.prototype.FadeOut = function (p, m, o) {
    if (this.FadeInfo) {
        this.FadeInfo.Timer.Stop();
    }

    this.FadeInfo = new FadeInfo(this, 0, p, m, o);
    this.FadeInfo.Timer.Start();
    return this;
}

Element.prototype.FadeTo = function (c, p, m, o) {
    if (this.FadeInfo) {
        this.FadeInfo.Timer.Stop();
    }

    this.FadeInfo = new FadeInfo(this, c, p, m, o);
    this.FadeInfo.Timer.Start();
    return this;
}

FadeInfo.Animate = function (f) {
    f.CurrentOpacity += f.IncrementOpacity;
    var o = f.CurrentOpacity.Round(-2);
    f.Element.Opacity(o);
    f.Element.Collapsed(o == 0);
    if ((f.IncrementOpacity > 0 ? f.CurrentOpacity >= f.StopOpacity : f.CurrentOpacity <= f.StopOpacity)) {
        f.Timer.Stop();
        f.Element.Opacity(f.StopOpacity);
        if (f.OnFinish) {
            f.OnFinish();
        }
        f.Element.FadeInfo = undefined;
    }
}

function FadeInfo(e, stopOpacity, p, m, o) {
    this.Element = e;
    this.StopOpacity = stopOpacity;
    this.CurrentOpacity = e.Opacity();
    p = p == undefined ? Speed.Normal : p;
    m = m == undefined ? Smoothness.Normal : m;
    this.OnFinish = o;
    this.IncrementOpacity = (stopOpacity - this.CurrentOpacity) / m;
    this.Timer = new Timer(new TimeSpan(0, 0, 0, p / m), true);
    this.Timer.SynchronousTrigger = false;
    this.Timer.OnTrigger.Add(new Delegate(FadeInfo.Animate, null, this));
}


Element.prototype.Unfold = function (d, p, m, o) {
    d = d === undefined ? Direction.Vertical : d;
    if (this.ResizesInfo) {
        this.ResizesInfo.Timer.Stop();
    }

    this.ResizesInfo = new ResizesInfo(this, d, 0, p, m, o);
    this.ResizesInfo.Timer.Start();
    return this;
}

Element.prototype.Fold = function (d, p, m, o) {
    d = d === undefined ? Direction.Vertical : d;
    if (this.ResizesInfo) {
        this.ResizesInfo.Timer.Stop();
    }

    this.ResizesInfo = new ResizesInfo(this, d, 1, p, m, o);
    this.ResizesInfo.Timer.Start();
    return this;
}

ResizesInfo.Animate = function (i) {
    if (i.IncrementWidth != 0) {
        i.CurrentWidth += i.IncrementWidth;
        i.Element.Width(i.CurrentWidth.Round(0));
    }

    if (i.IncrementHeight != 0) {
        i.CurrentHeight += i.IncrementHeight;
        i.Element.Height(i.CurrentHeight.Round(0));
    }


    if ((i.IncrementWidth > 0 ? i.CurrentWidth >= i.StopWidth : i.CurrentWidth <= i.StopWidth) && (i.IncrementHeight > 0 ? i.CurrentHeight >= i.StopHeight : i.CurrentHeight <= i.StopHeight)) {
        i.Timer.Stop();
        if (i.OnFinish !== undefined) {
            i.OnFinish();
        }
        i.Element.i = undefined;
        if (i.ResizesType == 1) {
            i.Element.Collapsed(true);
        }
        else {
            i.Element.Width("");
            i.Element.Height("");
        }
    }
}

function ResizesInfo(e, direction, type, p, m, o) {
    e.Collapsed(false);
    e.Style(Style.Overflow, "");

    this.StopWidth = e.Width();
    this.StopHeight = e.Height();
    this.Element = e;
    if (type == 0) {
        if (direction == Direction.Horizontal || direction == Direction.Diagonal) {
            var w = e.Width();
            e.Width("");
            this.StopWidth = e.Width();
            e.Width(0);
        }

        if (direction == Direction.Vertical || direction == Direction.Diagonal) {
            var h = e.Height();
            e.Height("");
            this.StopHeight = e.Height();
            e.Height(0);
        }
    }
    else {
        if (direction == Direction.Horizontal || direction == Direction.Diagonal) {
            this.StopWidth = 0;
        }

        if (direction == Direction.Vertical || direction == Direction.Diagonal) {
            this.StopHeight = 0;
        }
    }

    this.CurrentWidth = e.Width();
    this.CurrentHeight = e.Height();
    e.Style(Style.Overflow, "hidden");
    p = p == undefined ? Speed.Normal : p;
    m = m == undefined ? Smoothness.Normal : m;
    this.OnFinish = o;
    this.IncrementWidth = (this.StopWidth - this.CurrentWidth) / (m);
    this.IncrementHeight = (this.StopHeight - this.CurrentHeight) / (m);
    this.Timer = new Timer(new TimeSpan(0, 0, 0, p / m), true);
    this.Timer.OnTrigger.Add(new Delegate(ResizesInfo.Animate, null, this));
    this.Timer.SynchronousTrigger = false;
    this.ResizesType = type;
}

function ColorInfo(color, s, p, m, o) {
    this.StopRed = color.Red;
    this.StopGreen = color.Green;
    this.StopBlue = color.Blue;
    this.StopAlpha = color.Alpha;
    this.Element = e;
    this.Style = s;
    var c = e.Style(s);
    this.CurrentRed = c.Red;
    this.CurrentGreen = c.Green;
    this.CurrentBlue = c.Blue;
    this.CurrentAlpha = c.Alpha;

    p = p == undefined ? Speed.Normal : p;
    m = m == undefined ? Smoothness.Normal : m;
    this.OnFinish = o;
    this.IncrementRed = (this.StopRed - this.CurrentRed) / (p / m);
    this.IncrementGreen = (this.StopGreen - this.CurrentGreen) / (p / m);
    this.IncrementBlue = (this.StopBlue - this.CurrentBlue) / (p / m);
    this.IncrementAlpha = (this.StopAlpha - this.CurrentAlpha) / (p / m);
    this.Timer = new Timer(new TimeSpan(0, 0, 0, p / m), true);
    this.Timer.OnTrigger.Add(new Delegate(ColorInfo.Animate, null, this));
}

ColorInfo.Animate = function (c) {
    c.CurrentOpacity += fadeInfo.IncrementOpacity;
    c.Element.Opacity(fadeInfo.CurrentOpacity.Round(-2));
    if ((fadeInfo.IncrementOpacity > 0 ? fadeInfo.CurrentOpacity >= fadeInfo.StopOpacity : fadeInfo.CurrentOpacity <= fadeInfo.StopOpacity)) {
        fadeInfo.Timer.Stop();
        fadeInfo.Element.Opacity(fadeInfo.StopOpacity);
        if (fadeInfo.OnFinish) {
            fadeInfo.OnFinish();
        }
        fadeInfo.Element.FadeInfo = undefined;
    }
}

function Bounds() {
    this.Left = 0;
    this.Right = 0;
    this.Top = 0;
    this.Bottom = 0;
}

Bounds.prototype.Within = function (x, y) {
    return x.Between(this.Left, this.Right, true) && y.Between(this.Top, this.Bottom, true);
};

Bounds.prototype.OnEdge = function (x, y, edge, proximity) {
    proximity = proximity === undefined ? 1 : proximity;
    switch (edge) {
        case Edge.North:
            return y - this.Top <= proximity;

        case Edge.NorthWest:
            return y - this.Top <= proximity && x - this.Left <= proximity;

        case Edge.West:
            return x - this.Left <= proximity;

        case Edge.SouthWest:
            return this.Bottom - y <= proximity && x - this.Left <= proximity;

        case Edge.South:
            return this.Bottom - y <= proximity;

        case Edge.SouthEast:
            return this.Bottom - y <= proximity && this.Right - x <= proximity;

        case Edge.East:
            return this.Right - x <= proximity;

        case Edge.NorthEast:
            return y - this.Top <= proximity && this.Right - x <= proximity;
    }
};

Element.prototype.GetBounds = function () {
    var b = new Bounds();
    b.Left = this.X()
    b.Top = this.Y()
    b.Right = this.LayoutWidth() + b.Left;
    b.Bottom = this.LayoutHeight() + b.Top;
    return b;
};

Client.ElementClipped = function (e, side) {
    switch (side) {
        case Side.Top:
            return e.Y() < 0;

        case Side.Right:
            return e.X() + e.LayoutWidth() > Client.Width();

        case Side.Bottom:
            return e.Y() + e.LayoutHeight() > Client.Height();

        case Side.Left:
            return e.X();
    }
}

Element.prototype.Move = function (parentElement, side, alignment, xPad, yPad) {
    xPad = xPad === undefined ? 0 : xPad;
    yPad = yPad === undefined ? 0 : yPad;
    switch (side) {
        case Side.Top:
            this.Y(parentElement.Y() - this.LayoutHeight() - yPad);
            break;

        case Side.Bottom:
            this.Y(parentElement.Y() + parentElement.LayoutHeight() + yPad);
            break;

        case Side.Left:
            this.X(parentElement.X() - this.LayoutWidth() - xPad);
            break;

        case Side.Right:
            this.X(parentElement.X() + parentElement.LayoutWidth() + xPad);
            break;
    }

    switch (alignment) {
        case Alignment.Top:
            this.Y(parentElement.Y());
            break;

        case Alignment.Bottom:
            this.Y(parentElement.Y() + parentElement.LayoutHeight() - this.LayoutHeight());
            break;

        case Alignment.Middle:
            this.Y((parentElement.Y() + parentElement.LayoutHeight()) - (parentElement.Y() + parentElement.LayoutHeight() / 2));
            break;

        case Alignment.Left:
            this.X(parentElement.X());
            break;

        case Alignment.Right:
            this.X(parentElement.X() + parentElement.LayoutWidth() - this.LayoutWidth());
            break;

        case Alignment.Center:
            this.X((parentElement.X() + parentElement.LayoutWidth()) - (parentElement.X() + parentElement.LayoutWidth() / 2));
            break;

    }
}

Client.ElementClipped = function (e, side) {
    switch (side) {
        case Side.Top:
            return e.Y() < 0;

        case Side.Right:
            return e.X() + e.LayoutWidth() > Client.Width();

        case Side.Bottom:
            return (e.Y() + e.LayoutHeight()) - Client.VerticalScrollPosition() > Client.Height();

        case Side.Left:
            return e.X();
    }
}

Client.MoveElement = function (e, side) {
    switch (side) {
        case Side.Top:
            e.Y(0);
            break;

        case Side.Right:
            e.X(Client.Width() - e.LayoutWidth());
            break;

        case Side.Bottom:
            e.Y(Client.Height() - e.LayoutHeight());
            break;

        case side.Left:
            e.X(0);
            break;
    }
};

Content.prototype.ElementClipped = function (e) {
    var parentBounds = this.GetBounds();
    parentBounds.Left += this.HorizontalScrollPosition();
    parentBounds.Right += this.HorizontalScrollPosition();
    parentBounds.Top += this.VerticalScrollPosition();
    parentBounds.Bottom += this.VerticalScrollPosition();
    var childBounds = e.GetBounds();
    return !(childBounds.Left >= parentBounds.Left &&
             childBounds.Right <= parentBounds.Right &&
             childBounds.Top >= parentBounds.Top &&
             childBounds.Bottom <= parentBounds.Bottom);
}